%!PS-Adobe-3.0
%%BoundingBox: 0 0 800 3100

% Bounding box widths for comparison with real-world acrylic
% 850 for 30 cm wide material
% 867 for 12" wide material

%
% (c) 2017 Henner Zeller <h.zeller@acm.org>
%
% This file is part of LDGraphy http://github.com/hzeller/ldgraphy
%
% LDGraphy is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% LDGraphy is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with LDGraphy.  If not, see <http://www.gnu.org/licenses/>.

% Choices what to selectively cut. From bottom up.
% Use black or dark red transparent for all of these except the sled.
/print-bottom true def             % Bottom pane, holding drive-train
/print-bottom-drivetrain true def  % motor holder and bearing
/print-slide-rail-bottom true def  % The rails the sled is riding on.
/print-sled true def               % The sled moving the PCB along.
/print-slide-rail-top true def     % The rails the sled is riding under.
/print-sidepanels true def         % Side panels framing of the outer case.
/print-front-back true def         % Front and back parts of the case.
/print-toppanel true def           % The laser and rotating mirror mounted here.
/print-lid true def                % Lid covering everything.
/print-pcbframe true def           % frame around sled.

% Show measurement lines and descriptions. Switch these off before generating
% the DXF to save acrylic material (there are more gaps for the measurements)
/print-measures false def

% For testing while tweaking finger/slot output on your laser cutter.
% Typical tweak parameters are /acrylic-t, /do-reliefs, /relief-r, /slot-thick
/print-TEST-finger-slot false def
/print-TEST-x-spring false def

/mm { 72 25.4 div mul } bind def

% some basic functions
/max { 1 index 1 index lt { exch pop } { pop } ifelse } bind def
/tan { dup sin exch cos div } bind def

% User modifiable parameters.
/acrylic-t 3 mm def        % Material thickness.
/pcb-w-mm 100 def          % Size of PCB
/pcb-h-mm 160 def

% The scan angle we are covering. One one hand, we want to have this
% pretty small to minimize the focus lenght difference. But we also don't
% want to have it too small, as this means we have lower on-time hence longer
% exposure time. Maximum: 120. Typical near 40 degree.
/scan-angle 40 def                        % Something < 120, better less than 60
/scan-redirect-mirror-distance 60 mm def  % Mirror distance
/scan-redirect-mirror-base 30 mm def      % Width of the mirror foot.
/scan-redirect-mirror-width 15 mm def     % Width redirect mirror itself.

% temporary while hacking on prototype to see influence of different angles.
/do-angle-test false def

% reliefs on inner edges for finger/slots.
/do-reliefs true def
/relief-r 0.18 mm def

/slot-thick acrylic-t def  % slot thickness for snug fit.

/screw-r 3.1 mm 2 div def
/rod-r 4.7 mm 2 div def
/finger-len 20 mm def
/connect-screw-len 4.5 mm def

/pcb-w pcb-w-mm mm 2 mm add def
/pcb-h pcb-h-mm mm 2 mm add def

/bed-w pcb-w acrylic-t 2 mul add 10 mm add def    % rail + 5 mm extra on each edge
/bed-h pcb-h 10 mm add def

% derived from above.
/bed-pcb-off-w bed-w pcb-w sub 2 div def
/bed-pcb-off-h bed-h pcb-h sub 2 div def

/bed-slide-high-bottom 29 mm def
/bed-slide-high-top 8 mm def
/slide-hole-count 4 def

% how high the front part should be. Essentially it should end shy below
% where the bed tray emerges.
/front-cover-high bed-slide-high-bottom 1 mm sub def

/drive-train-fingers 11 mm def   % finger distance for drive train on sled
/stepper-len 36 mm def
/stepper-height 28.2 mm def       % nema11
/stepper-mount-screw 11.5 mm def  % screw distance measured from center
/stepper-screw-dia 2.5 mm def     % M2.5 for nema11
/stepper-center-plate-dia 22.5 mm def

/acme-dia 5 mm def            % acme screw diameter

/drive-mount-pos stepper-len def  % stepper mount position seen from back
/drive-screw-start 35 mm 10 mm max def      % axle to where acme screw starts. Coupler
/bearing-mount-pos
    stepper-len drive-screw-start add  % starting from here
    pcb-h add                          % .. the amount we need to move
    acrylic-t 1.5 mul add              % space for drive-train holder
    8 mm add                           % Provide a little more room
    def  % acme bearing

% the corresponding assembly on the sled
/bed-drive-screw-w acrylic-t def      % distance between driving holders
% outer radius of hex-nut driving the thing. Since it is easier to measure, we
% base it on the distance between two oppsing flat ends.
/bed-drive-screw-r 9.33 mm 0.866 div 2 div def

/bed-drive-wobble 0.5 mm def  % amount of 'wobble' we should give the drive screw

% TODO: the following foremost needs to also take into account where the board
% hits the slot.
/bed-drive-pos stepper-len drive-screw-start add def  % where the drive position relative from back.
/bed-drive-screw-top
     bed-slide-high-bottom stepper-height sub  % amount hovering about stepper
     stepper-height 2 div add
     def
/bed-drive-w bed-w 2 div def
/bed-drive-h bed-drive-screw-top bed-drive-screw-r add 2 mm add def

% we are placing the switches as close as possible to the center of the
% drive train to not have too much axial force (the microswitches in question
% use a little force). Otherwise the corner would of course be nice.
/end-switch-offset 0.30 bed-w mul def  % from center
/end-switch-thick 8 mm def             % thickness of switch
/end-switch-from-back-of-sled drive-mount-pos end-switch-thick add acrylic-t add def
/end-switch-trigger-len                %  plastic trigger on sled
    bed-drive-pos acrylic-t add end-switch-thick sub
    drive-mount-pos end-switch-thick add
    sub def

% Laser slot distance as constrained by the sled.
/laser-slot-distance pcb-h bed-h pcb-h sub 2 div add 3 mm add def
/laser-slot-h 10 mm def
/laser-slot-w pcb-w 2 mm add def

% Hsync diffusor
/hsync-len laser-slot-h def
/hsync-w 2 mm def

% The UV laser case
/laser-width 33 mm def                % Typical width.
/laser-length 55 mm def               % Typical length of the laser box
/laser-edge-mount-distance 18 mm def  % distance center of laser from edge.

/polygon-mirror-high laser-width 2 div def  % == center of the laser
/polygon-mirror-w 44 mm def
/polygon-mirror-h 60 mm def
/polygon-mirror-r 35 mm 2 div def

% When things are tight on the toppanel, we might need to shift the polygon
% mirror a little up to not conflict with the laser. Otherwise, 0 mm is best.
% (TODO: this is not angle corrected yet for anything non-zero).
/polygon-mirror-offset-y 0 mm def

% As well, if there is not enough space on the toppanel, we might need to rotate
% the assembly (e.g. for 70mmx100mm PCBs, a 90 degree change is in order)
/polygon-mirror-rotation 0 def

% The down mirror is elevated above the top plate, so we need the acrylic
% thickness to be on the same level as the rot mirror that is mounted on
% the top plate. TODO: adding acrylic-t here is confusing.
/down-mirror-high polygon-mirror-high acrylic-t add def
/down-mirror-w 10 mm def
/down-mirror-t 4.3 mm def
/down-mirror-slot false def            % if we want a slot for an overlong mirror
/down-mirror-backing-width 15.5 mm def  % might be calculated from curvature.
/down-mirror-backing-glue-thick 0 mm def

% Given that we cover 60 degrees, we cover 2 * sin(30) times radius in
% horizontal direction, i.e. radius == pcb-w. We want to use a little less of
% the range to have space for synchronization.
/arc-radius pcb-w 20 mm add def

% We want to be at least half pcb sled longer so that we are supported
% at center of gravity. However, the mechanics might require to be longer.
/inner-device-len
   laser-slot-distance bed-h 2 div add
   bearing-mount-pos acrylic-t add 5 mm add
   max
   def
/inner-device-high bed-slide-high-bottom bed-slide-high-top add acrylic-t add def

/mirror-holder-width 20 mm def  % width of the 45 degree down mirror holder
/mirror-holder-r mirror-holder-width 2 div def
/mirror-holder-high down-mirror-high mirror-holder-r add def

% Apron above the toppanel. derived value
/top-apron-high
    mirror-holder-high
    acrylic-t sub          % Want to embed down again around the top angle

    laser-width            % Whatever is higher. Laser needs to fit as well.
    max
    def

% -- variables to place properly between cuts
% distance between different cuts
/cut-dist print-measures { 6 mm } { 1 mm } ifelse def
/side-cut-distance inner-device-high acrylic-t 2 mul add top-apron-high add
     2 mul cut-dist add def

% Linewidth doesn't do anything to the DXF output, it just makes it easier
% to manually inspect in the PostScript viewer.
1 setlinewidth


% Like currentpoint but returns a boolean to describe if there was a point.
% Apparantly, PostScript does not have a predicate to ask for a current path,
% so we have to tap into the error message.
% currentpoint? x y true   % if there is a current point
% currentpoint? false      % if there isn't
/currentpoint? {
    2 dict begin
      /has-currentpoint true def
      /old-handler errordict /nocurrentpoint get def
      errordict /nocurrentpoint { pop /has-currentpoint false def} put
      currentpoint
      has-currentpoint
      errordict /nocurrentpoint currentdict /old-handler get put
    end
} def

/-gsave /gsave load def
/gsave {
    currentpoint? {
	% Ideally, we never reach this, but all paths are properly stroke'd
	% when gsave is called. But it is hard to find mis-uses in the
	% stack-trace gs provides. So just do the right thing and stroke
	% here to repair the situation.
	% (also, perfectly benign to have something with no path just a point)
	% The following will help debug it
	%-gsave /Helvetica findfont 10 scalefont setfont (<- Here not properly stroked) show grestore
	stroke
        -gsave
	moveto
    } {
	-gsave
    } ifelse
} def

%%-- measureline.ps

% Measure-line features. It is a little hard to get things PostScript files
% included depending on context. So we include it here manually.
/measure-fontsize 5 mm def
/measure-font /Helvetica-Bold findfont measure-fontsize scalefont def
/measure-desc-font /Helvetica findfont 4 mm scalefont def
/measure-dict << >> def
/measure-col { 0.7 0.7 0.7 setrgbcolor } def

% (desc) measure-offset dx dy
/measure-to {
    2 copy atan rotate
    5 dict begin
    dup mul exch dup mul add sqrt /len exch def
    /offset exch def
    /desc-text exch def
    /display-len len 72 div 25.4 mul 100 mul round 100 div def
    /arrow-len 3 mm def
    /arrow-w 1 mm def
    /measure-unit () def   % mm took too much space

    %0 offset moveto len offset lineto

    % Without offset, add at least a little measurement stub.
    offset 0 eq {
      0 offset -0.5 mm add moveto   0 offset 0.5 mm add lineto
      len offset -0.5 mm add moveto len offset 0.5 mm add lineto
    } if

    % Arrow between lines
    0 offset moveto arrow-len offset arrow-w add lineto
    0 offset moveto arrow-len offset arrow-w neg add lineto

    len offset moveto len arrow-len sub offset arrow-w add lineto
    len offset moveto len arrow-len sub offset arrow-w neg add lineto

    0 0 moveto 0 offset lineto
    len 0 moveto len offset lineto

    display-len (         ) cvs
    dup stringwidth pop measure-unit stringwidth pop add 2 div /text-w-half exch def

    0 offset moveto len 2 div text-w-half sub 0.5 mm sub offset lineto

    len 2 div text-w-half sub         % x
    offset measure-fontsize 3 div sub moveto show measure-unit show
    len 2 div text-w-half add 0.5 mm add offset moveto
    len offset lineto

    measure-desc-font setfont
    len 2 div desc-text stringwidth pop 2 div sub offset measure-fontsize sub moveto
    desc-text show
    end
    stroke
} def

% (desc) measure-offset x1 y1 x2 y2
/measure-line {
    4 2 roll
    2 copy translate
    % 2=20 20 1=20 10
    3 -1 roll exch  % 20 20 10 20
    sub        % 20 20 dx
    3 1 roll sub % dx dy
    measure-to
} def

% pos-x pos-y
/measure-A {
    % array stores [(desc) line-offset x1 y1 x2 y2]
    userdict /current-measure [() 0  0 0  0 0] put
    current-measure 2 3 index put   % x
    current-measure 3 2 index put   % y
} def
/current-measure-A {
    currentpoint measure-A pop pop
} def

% pos-x pos-y [optional-array-with-offset]
/measure-B {
    dup type (arraytype) eq {
	dup 0 get   % get offset
	current-measure 1 3 -1 roll put
	dup length 1 gt {  % get description string if available
	    dup 1 get
 	    current-measure 0 3 -1 roll put
	} if
	pop
    } if
    current-measure 4 3 index put   % x
    current-measure 5 2 index put   % y
    measure-dict measure-dict length current-measure put  % now append at end.
} def
/current-measure-B {
    count 0 gt {
      dup type (arraytype) eq {
	dup 0 get   % get offset
	current-measure 1 3 -1 roll put
	dup length 1 gt {  % get description string if available
	    dup 1 get
 	    current-measure 0 3 -1 roll put
	} if
	pop
      } if
    } if
    currentpoint measure-B pop pop
} def

/clear-measures {
    userdict /measure-dict << >> put
} def

/flush-measures {
    stroke   % Flush whatever is to be drawn, otherwise ghostscript does it twice
    print-measures {
      gsave
      0.5 setlinewidth
      measure-col
      measure-font setfont
      measure-dict {
        aload pop   % put array content on stack. Get rid of array
        gsave measure-line grestore
        pop         % get rid of dict key.
      } forall
      grestore
    } if
    clear-measures
} def

%%-- measureline.ps

% width clip-min-x clip-max-x x y
/wave-bend {
    gsave
    6 dict begin
    /y exch def
    /x exch def
    x sub /clip-max-x exch def  % substracting x as we are going to translate
    x sub /clip-min-x exch def
    /total-len exch def
    /relief-radius 0.2 mm def
    x y translate
    0 clip-min-x gt { 0 0 relief-radius 0 360 arc } if
    0 0 moveto
    0 1 40 {
	40 div /fraction exch def
	/angle fraction total-len mul len-x div 360 mul 2 mul def
	/xpos total-len fraction mul def

	xpos angle sin 0.5 mm mul
	xpos clip-min-x lt xpos clip-max-x gt or { moveto } { lineto } ifelse
    } for
    currentpoint pop clip-max-x lt { currentpoint relief-radius 0 360 arc } if
    end
    stroke
    grestore
} def

/kerf-bend-steps 2 def
/lh-spacing-x 2 mm def
/lh-spacing-y 1.9 mm def

% width height fun
/kerf-bend {
  gsave
  currentpoint translate
  1 dict begin
  /line-fun exch def
  /lh-h exch def
  /lh-w exch def
  /len-x lh-w lh-spacing-x sub kerf-bend-steps div def

  % this clipping doesn't really seem to work with pstoedit.
  %0 0 moveto
  %lh-w 0 rlineto
  %0 lh-h rlineto
  %lh-w neg 0 rlineto
  %0 lh-h neg rlineto
  %closepath
  %clip

  newpath
  lh-spacing-y 2 mul lh-spacing-y 2 mul lh-h {
     /y exch def
     0 1 kerf-bend-steps 1 sub {
	/i exch def
	/x i len-x mul lh-spacing-x add def
	len-x lh-spacing-x sub  % width
	-1 mm lh-w 2 mm add                 % x-min/ x-max
	x y line-fun
     } for
  } for
  stroke

  lh-spacing-y lh-spacing-y 2 mul lh-h lh-spacing-y sub {
     /y exch def
     0 1 kerf-bend-steps {
	/i exch def
	/x i len-x mul len-x 2 div sub lh-spacing-x add def
	len-x lh-spacing-x sub  % width
	-1 mm lh-w 2 mm add                 % x-min/ x-max
	x y line-fun
     } for
  } for
  stroke
  end
  grestore
} def

/inner-cut { 1 0 0 setrgbcolor } def
/outer-cut { 0 0 0 setrgbcolor } def
/simple-cut { 1 1 0 setrgbcolor } def
/scan-engraving-cut { 0 1 0 setrgbcolor } def
/vector-engraving-cut { 0 0 1 setrgbcolor } def

% (description) x y
/describe {
    print-measures {
      -gsave
      /Helvetica findfont 8 mm scalefont setfont
      measure-col
      moveto
      show
      grestore
    } if
} def

% r
/currentpoint-circle {
    1 dict begin
    /crr exch def
    currentpoint
    crr 0 rmoveto
    crr 0 360 arc
    crr neg 0 rmoveto
    end
} def

% x y r
/circle {
    3 1 roll moveto currentpoint-circle
} def

% Given a direction in delta coordinats and a length, return the new
% coordinates where that vector points to.
% dir_x dir_y length  -> dx dy (relative position)
/colinear {
	5 dict begin
       /thick exch  def
       /dy exch def
       /dx exch def
       /len dx dx mul dy dy mul add sqrt def
       /fraction thick len div def  % fraction of amount that is perpendicular
       dx fraction mul dy fraction mul
       end
} def

% Similar to colinear, but yields the coordinates of the normal of the given
% length, so perpendicular to the original coordinates in delta.
% dir_x dir_y length  -> dx dy (relative position)
/perpendicular {
	5 dict begin
       /thick exch  def
       /dy exch def
       /dx exch def
       /len dx dx mul dy dy mul add sqrt def
       /fraction thick len div def  % fraction of amount that is perpendicular
       dy fraction mul neg dx fraction mul
       end
} def

% dx dy thick
/orth-segment {
	4 dict begin
       /thick exch  def
       /is-outer thick 0 gt def
       /dy exch def
       /dx exch def
       dx dy thick perpendicular rlineto is-outer { relief-here } if
       dx dy rlineto                     is-outer { relief-here } if
       dx dy thick neg perpendicular rlineto
       end
} def

% dx dy  % create a hole half way down the dx dy
/connect-hole {
    2 dict begin
    /sdy exch def
    /sdx exch def
    sdx 2 div sdy 2 div rmoveto
    sdx sdy thick 2 div perpendicular rmoveto
    currentpoint 1 mm circle
    sdx sdy thick 2 div neg perpendicular rmoveto
    sdx 2 div neg sdy 2 div neg rmoveto
    end
} def

% dx dy  % create a tslot half way down the dx dy
/connect-t-slot {
  3 dict begin
  /sdy exch def
  /sdx exch def
  /ff finger-len 2 mm sub finger-len div 2 div def
  sdx ff mul sdy ff mul rmoveto
  sdx sdy -0.5 mm perpendicular rmoveto
  sdx sdy connect-screw-len perpendicular rlineto
  sdx sdy -1 mm colinear rlineto
  relief-here
  sdx sdy 1.6 mm perpendicular rlineto
  sdx sdy 1 mm colinear rlineto
  sdx sdy connect-screw-len 2 div perpendicular rlineto
  sdx sdy 2 mm colinear rlineto
  sdx sdy connect-screw-len 2 div neg perpendicular rlineto
  sdx sdy 1 mm colinear rlineto
  sdx sdy -1.6 mm perpendicular rlineto
  currentpoint stroke moveto
  relief-here
  sdx sdy -1 mm colinear rlineto
  sdx sdy connect-screw-len neg perpendicular rlineto
  currentpoint stroke moveto
  % go back
  sdx sdy 0.5 mm perpendicular rmoveto
  sdx sdy 2 mm neg colinear rmoveto
  sdx ff mul neg sdy ff mul neg rmoveto
  end
} def

% x y thick
/finger-groove-rlineto {
    12 dict begin
    /thick exch def
    /is-inner thick 0 lt def
    /dy exch def
    /dx exch def
    /len dx dx mul dy dy mul add sqrt def
    /factor acrylic-t len div def  % perpendicular thing.
    /segments
         len finger-len sub  % we want one finger len at the end
	 finger-len 2 mul    % number of low and up segments
	 div floor cvi def
    /extra-fraction len finger-len sub
         segments finger-len 2 mul mul sub len div 2 div def
    dx extra-fraction mul dy extra-fraction mul rlineto
    /sdx dx finger-len len div mul def
    /sdy dy finger-len len div mul def
    1 1 segments {
	pop
	sdx sdy rlineto            is-inner { relief-here } if
	sdx sdy thick orth-segment is-inner { relief-here } if
    } for
    sdx sdy rlineto  % one extra bottom segment.
    dx extra-fraction mul dy extra-fraction mul rlineto
    end
} def

% x y thick do_hole { 0, 1, 2 }
/hole-slot-rlineto {
    12 dict begin
    /do-hole exch def
    /thick exch def
    /dy exch def
    /dx exch def
    /len dx dx mul dy dy mul add sqrt def
    /factor acrylic-t len div def  % perpendicular thing.
    /segments
         len finger-len sub  % we want one finger len at the end
	 finger-len 2 mul    % number of low and up segments
	 div floor cvi def
    /extra-fraction len finger-len sub
         segments finger-len 2 mul mul sub len div 2 div def
    dx extra-fraction mul dy extra-fraction mul rmoveto
    /sdx dx finger-len len div mul def
    /sdy dy finger-len len div mul def
    1 1 segments {
	pop
	do-hole 1 eq {  % hole
	    sdx sdy connect-hole
	} if
	do-hole 2 eq {  % t-slot
	    sdx sdy connect-t-slot
	} if
	sdx sdy rmoveto
	sdx sdy rmoveto
    } for

    do-hole 1 eq {  % hole
      sdx sdy connect-hole
    } if
    do-hole 2 eq {  % t-slot
      sdx sdy connect-t-slot
    } if
    sdx sdy rmoveto
    dx extra-fraction mul dy extra-fraction mul rmoveto
    end
} def

% dx dy
/t-slot-rlineto {
    acrylic-t neg 2 hole-slot-rlineto
} def
/hole-rlineto {
    acrylic-t neg 1 hole-slot-rlineto
} def

% dx dy
/finger-rlineto {
    2 copy t-slot-rlineto 2 copy neg exch neg exch rmoveto
    acrylic-t neg finger-groove-rlineto
} def

/groove-rlineto {
    2 dict begin
    /dy exch def
    /dx exch def
    dx dy hole-rlineto dx neg dy neg rmoveto
    dx dy acrylic-t neg perpendicular rlineto
    dx dy acrylic-t finger-groove-rlineto
    dx dy acrylic-t perpendicular rlineto
    end
} def

/finger-rlineto-noscrew {
    acrylic-t neg finger-groove-rlineto
} def

/groove-rlineto-noscrew {
    2 dict begin
    /dy exch def
    /dx exch def
    dx dy acrylic-t neg perpendicular rlineto
    dx dy acrylic-t finger-groove-rlineto
    dx dy acrylic-t perpendicular rlineto
    end
} def

% Given an absolute target coordinate, converts that to a relative call.
% xt yt proc
/lineto-to-rlineto-proc {  % need: (xt - x) (yt - y)
    3 1 roll
    currentpoint  % xt yt x y
    3 -1 roll exch sub  % xt yt x y -> xt x y yt -> xt x yt y -> xt x (yt - y)
    3 1 roll sub  % dy dx
    exch
    3 -1 roll exec
} def

% Same finger/groove commands as above, but with lineto
/finger-lineto { { finger-rlineto } lineto-to-rlineto-proc } def
/groove-lineto {  { groove-rlineto } lineto-to-rlineto-proc } def
/finger-lineto-noscrew {  { finger-rlineto-noscrew } lineto-to-rlineto-proc } def
/groove-lineto-noscrew {  { groove-rlineto-noscrew } lineto-to-rlineto-proc } def

% dx dy
/slot-rlineto {
    gsave
    inner-cut   % inside kerf correction.
    9 dict begin
    /dy exch def
    /dx exch def
    /len dx dx mul dy dy mul add sqrt def
    /segments
         len finger-len sub  % we want one finger len at the end
	 finger-len 2 mul    % number of low and up segments
	 div floor cvi def
    /extra-fraction len finger-len sub
         segments finger-len 2 mul mul sub len div 2 div def

    currentpoint translate dy dx atan rotate

    /dx len def
    /dy 0 def
    dx extra-fraction mul dy extra-fraction mul rmoveto
    /sdx dx finger-len len div mul def
    /sdy dy finger-len len div mul def
    1 1 segments {
	pop
	sdx sdy rmoveto
	currentpoint
	sdx slot-thick relief-box
	moveto sdx 0 rmoveto
    } for
    stroke
    print-measures {
	measure-col
	0 0 moveto len 0 lineto stroke
	[3] 0 setdash
	0 0 moveto 0 acrylic-t lineto len acrylic-t lineto len 0 lineto stroke
    } if
    end
    grestore
} def

/slot-lineto {  % need: (xt - x) (yt - y)
    currentpoint  % xt yt x y
    3 -1 roll exch sub  % xt yt x y -> xt x y yt -> xt x yt y -> xt x (yt - y)
    3 1 roll sub  % dy dx
    exch
    slot-rlineto
} def

% Spring edge of a part. Really only works in x-direction right now
% x y len
/x-spring {
  7 dict begin
 /line-len exch def     % total length of line
 2 copy  % so that we later can move to the end of the line

 % Some hardcodes, probably would be good as
 /arc-x 5 mm def        % curve up and down part
 /arc-y 0.7 mm def      % thickness of the 'hump'
 /spring-thick 2 mm def % thickness of the bendable part
 /spring-short 2 mm def % how much shorter the bendable part should be to length
 /bend-radius 1.2 mm def
 /overcut arc-y def     % cut starting outside edge

  gsave
  outer-cut
  translate 0 0 moveto
  line-len arc-x 2 mul sub 0 mm lineto    % start of hump
  arc-x 2 div   0                         % hump up
  arc-x 2 div   arc-y
  arc-x arc-y rcurveto

  arc-x 2 div 0                           % hump down
  arc-x 2 div arc-y neg
  arc-x arc-y neg rcurveto
  stroke

  % Now, cut out the area that will provide the springiness
  simple-cut
  line-len overcut moveto
  line-len spring-thick neg lineto              % down; should be arc

  % line backwards to form spring span; might be better with bezier ?
  line-len spring-short sub bend-radius sub neg 0 rlineto
  currentpoint bend-radius sub bend-radius 90 270	 arc

  % at the end of the travel, we bend down by this amount. Since the 'hump' of
  % height arc-y is a little bit inwards of the lever, the travel at the end is
  % more than arc-y
  /down-bend line-len line-len arc-x sub div arc-y mul 0.2 mm add def

  line-len 0.5 mm add
  spring-thick neg down-bend sub lineto

  0 spring-thick down-bend add overcut add rlineto
  stroke
  grestore

  % Go to the end of the spring, so that this just behaves as a line segment
  exch line-len add exch moveto
  end
} def

% r is the outer radius.
% n r
/n-polygon {
    gsave
    currentpoint translate
    3 dict begin
    /r exch def
    /n exch def
    /angle 360 n div def
    r 0 moveto
    1 1 n {
	dup
	angle mul cos r mul
	exch
	angle mul sin r mul
	lineto
    } for
    closepath stroke
    end
    grestore
} def

/end-switch {
    currentpoint
    0 0 1 mul rmoveto 0.6 mm currentpoint-circle
    0 5.02 mm rmoveto 0.6 mm currentpoint-circle
    0 5.02 mm rmoveto 0.6 mm currentpoint-circle stroke
    %moveto currentpoint
    %engraving-cut -2.6 mm -2.6 mm rmoveto 5.2 mm 2.54 mm 6 mul box stroke
    %outer-cut
    moveto
} def

/hsync-slot {
    0 hsync-w 2 div neg rmoveto
    hsync-len hsync-w box
} def

% x y
/photo-diode-cutout {
    inner-cut
    moveto
    0 2.5 mm rlineto
    -3.7 mm 0 rlineto
    0 0.5 mm rlineto
    -1.1 mm 0 rlineto

    0 -1.4 mm rlineto  % lead cutout
    -4 mm 0 rlineto
    0 -3.2 mm rlineto
    4 mm 0 rlineto
    0 -1.4 mm rlineto

    1.1 mm 0 rlineto
    0 0.5 mm rlineto
    3.7 mm 0 rlineto
    closepath
    currentpoint
    stroke
    moveto hsync-slot stroke
    outer-cut
} def

/screw {
    screw-r circle
} def

% To leave a little more space. Only in X direction currently.
/oval-screw {
    3 dict begin
    2 div /half-extend exch def
    /y exch def
    /x exch def
    x half-extend add y screw-r add moveto
    x half-extend sub y screw-r add lineto
    x half-extend sub y screw-r 90 270 arc
    x half-extend add y screw-r sub lineto
    x half-extend add y screw-r -90 90 arc
    end
} def

% Initially, we had rods through the hole width, now just simple
% screws.
/rod {
    screw-r circle
} def

/relief-here {
    do-reliefs { relief-r currentpoint-circle } if
} def

% make a box at current position with given width/height
% w h -> -
/box {
  2 dict begin
  /h exch def
  /w exch def

  w 0 rlineto
  0 h rlineto
  w neg 0 rlineto
  0 h neg rlineto
  closepath stroke
  end
} def

% Like box, but just take the measurements from the stack without drawing
% the box.
% w h -> w h
/measure-box {
  2 dict begin
  /h 1 index def
  /w 2 index def

  current-measure-A currentpoint exch w add exch [-3 mm] measure-B pop pop
  current-measure-A currentpoint h add [3 mm] measure-B pop pop
  end
} def

% like box, but with corner reliefs.
/relief-box {
  2 dict begin
  /h exch def
  /w exch def

  w 0 rlineto      relief-here
  0 h rlineto      relief-here
  w neg 0 rlineto  relief-here
  0 h neg rlineto  relief-here
  closepath stroke

  end
} def

% w h radius
/rounded-corner-box {
   3 dict begin
   /radius exch def
   /height exch def
   /width exch def

   currentpoint
   stroke   % need to flush state before gsave/grestore
   gsave
   translate
   0 radius add 0 moveto
   width radius sub 0 lineto
   width radius sub 0 radius add radius -90 0 arc

   width height radius sub lineto
   width radius sub height radius sub radius 0 90 arc

   0 radius add height lineto
   0 radius add height radius sub radius 90 180 arc

   0 0 radius add lineto
   0 radius add 0 radius add radius 180 270 arc
   closepath stroke
   grestore
   end
} def

% Origin is rotation axis
% Boolean if outline should be printed
% bool x y
/polygon-mirror-footprint {
    stroke
    gsave
    translate
    0 0 moveto
    currentpoint 7 mm circle
    41.6 mm 2 div 40 mm screw
    41.6 mm 2 div neg 40 mm screw
    41.6 mm 2 div -18.5 mm screw
    41.6 mm 2 div neg -18.5 mm screw
    stroke

    % Some hinting lines.
    print-measures {
	measure-col
	-24 mm -24 mm moveto 48 mm 68 mm box
	0 0 polygon-mirror-r circle stroke
    } if

    outer-cut
    48 mm 2 div neg -24 mm translate

    % should we print the outline ?
    {
      0 0 moveto 48 mm 68 mm box % cuts the outer of the mirror
      48 mm 15 mm sub 2 div 0 moveto 15 mm 5 mm box  %  sensor protrusion cutout
    } if
    stroke

    grestore
} def

/beaglebone-black-footprint {
    currentpoint
    gsave
    translate
    measure-col
    86.36 mm 54.61 mm 12.7 mm rounded-corner-box
    -3 mm 21.325 mm moveto 20 mm 17 mm box   % Ethernet jack
    inner-cut
    14.61 mm                       3.18 mm     1.8 mm circle
    14.61 mm              54.61 mm 3.18 mm sub 1.8 mm circle
    86.36 mm 5.59 mm sub           6.35 mm     1.8 mm circle
    86.36 mm 5.59 mm sub  54.61 mm 6.35 mm sub 1.8 mm circle
    stroke
    grestore
} def

% Laser footprint at current position. Origin is the laser exit
/laser-footprint {
    currentpoint
    gsave
    translate
    print-measures {
      measure-col  % outline colors
      laser-length neg laser-width 2 div neg moveto
      laser-length laser-width box                         % Metal box of laser
      laser-length 12 mm add neg laser-width 2 div 2 mm sub neg moveto
      12 mm laser-width 4 mm sub box                       % Cooling fan

      0 0 moveto 60 mm 0 rlineto   % demo laser direction
      stroke
    } if

    inner-cut
    -7.85 mm 0 2 mm oval-screw
    laser-length neg 7.85 mm add 0 2 mm oval-screw
    -16 mm 0 2 mm oval-screw
    -36 mm 0 2 mm oval-screw stroke
    grestore
    outer-cut
} def

% A nema 11 footprint.
/motor-footprint {
    currentpoint
    gsave
    translate
    measure-col
    0 -11 mm moveto 3 mm 22 mm box      % shim
    3 mm -2.5 mm moveto 18 mm 5 mm box  % shaft
    -32 mm -14 mm moveto 32 mm 28 mm box % body
    stroke
    grestore
} def

% (text)
/center-show {
    dup stringwidth pop 2 div neg 0 rmoveto
    show
} def

% height screw-r
/slide-holes {
    2 dict begin
    /sr exch def
    /center-height exch 2 div def
    inner-device-len slide-hole-count div 2 div
    inner-device-len slide-hole-count div
    inner-device-len {
	center-height sr circle
    } for
    stroke
    end
} def

%---- end help procedures, start parts.

/polygon-mirror-assembly {
    6 dict begin

    % Origin of the scan point, essentially the point light hits the polygon
    % mirror in the center axis.
    /scan-origin-to-board pcb-w 2 div scan-angle 2 div tan div def
    /scan-origin-to-slot
       scan-origin-to-board       % total
       polygon-mirror-high sub    % down from laser height on surface
       acrylic-t sub              % through top part 'ceiling'
       bed-slide-high-top sub     % Down to top of sled
       def

    % X/Y coordinate where the laser should hit the polygon mirror.
    /poly-mirror-y bed-w 2 div polygon-mirror-offset-y add def
    /poly-mirror-x laser-slot-distance scan-origin-to-slot sub def

    poly-mirror-x poly-mirror-y measure-A pop pop
    laser-slot-distance bed-w 2 div [0 (scan-origin-to-slot)] measure-B pop pop

    poly-mirror-x poly-mirror-y measure-A pop pop
    poly-mirror-x scan-redirect-mirror-distance add
    laser-edge-mount-distance [0 (<-redirected)] measure-B pop pop

    /redirect-angle
        poly-mirror-y laser-edge-mount-distance sub
	scan-redirect-mirror-distance
	atan
        def

    % Redirect mirror placement.
    gsave
	/finger-len 3 mm def
	/mirror-thickness 1 mm def  % TODO: measure and use angle
	poly-mirror-x scan-redirect-mirror-distance add
	laser-edge-mount-distance translate
	90 redirect-angle 2 div sub rotate
	scan-redirect-mirror-base 2 div mirror-thickness neg moveto
	scan-redirect-mirror-base 2 div neg mirror-thickness neg slot-lineto stroke
	print-measures {
  	  % illustrate normal on that mirror
	  measure-col [3] 0 setdash 0 0 moveto 0 20 mm lineto stroke
	} if
    grestore

    % FYI help-lines showing the polygon mirror when it projects to center.
    print-measures {
       gsave
 	poly-mirror-x poly-mirror-y translate
	90 redirect-angle 2 div sub rotate
	measure-col
	-10 mm 0 moveto 10 mm 0 lineto stroke
	% Illustrate mirror normal.
	[3] 0 setdash 0 -20 mm moveto 0 polygon-mirror-r lineto stroke
        grestore
    } if

    /hit-mirror-offset scan-origin-to-slot scan-angle 2 div tan mul def

    poly-mirror-x poly-mirror-y measure-A pop pop
    laser-slot-distance bed-w 2 div hit-mirror-offset add [0 (v)] measure-B pop pop

    poly-mirror-x poly-mirror-y measure-A pop pop
    laser-slot-distance bed-w 2 div hit-mirror-offset sub [0 (^)] measure-B pop pop

    /dx redirect-angle 2 div cos polygon-mirror-r mul def
    /dy redirect-angle 2 div sin polygon-mirror-r mul def

    gsave
    poly-mirror-x dx sub       poly-mirror-y dy add   translate
    polygon-mirror-rotation rotate
    % mounting holes for rotating mirror
    false  % do not cut outline
    0 0 polygon-mirror-footprint stroke

    grestore
    end
} def

/part-toppanel {
    (Toppanel) 30 mm 5 mm describe
    /ldgraphy-font-size 22 mm laser-slot-w mul 104 mm div def

    % bottom area
    newpath
    0 0 moveto
    current-measure-A

    % right to mirror slot
    laser-slot-distance 0 finger-lineto-noscrew

    % skipping the curved area over the laser slot
    laser-slot-distance top-apron-high 2 div add
    0 lineto

    % to the end on the right.
    inner-device-len 0 finger-lineto-noscrew

    [-6 mm (inner-device-len)] current-measure-B

    % up. Curved or with circles.
    true {
       % Curved
       3 dict begin
       /distance-from-end
           inner-device-len
	   laser-slot-distance sub
	   top-apron-high 2 div sub
	   acrylic-t sub  % plug in the rounded top part
	   4 mm sub       % some sanity spacing
	   %ldgraphy-font-size sub % TODO: remove when text placed differently
	   def
       /ease-in-fraction 0.2 def
       /center-pull 1.1 def

       inner-device-len distance-from-end sub bed-w 2 div measure-A pop pop
       inner-device-len bed-w 2 div measure-B pop pop

       inner-device-len bed-w ease-in-fraction mul % control #1
       inner-device-len distance-from-end sub bed-w center-pull bed-w mul sub %#2
       inner-device-len distance-from-end sub bed-w 2 div  % center
       curveto

       inner-device-len distance-from-end sub bed-w center-pull mul % control #1
       inner-device-len bed-w 1 ease-in-fraction sub mul            % control #2
       inner-device-len bed-w
       curveto
       end
    } {
       % Circles
       1 dict begin
       /ease-in-r bed-w 10 div def
       inner-device-len ease-in-r sub 0 ease-in-r 0 90 arc
       inner-device-len ease-in-r sub          % center: on right edge
       bed-w 2 div               % .. in the middle
       bed-w 2 div ease-in-r sub     % radius a bit smaller. Will add a line
       270 90 arcn
       inner-device-len ease-in-r sub bed-w ease-in-r 270 360 arc
       end
    } ifelse

    % front part back to end of rounding of apron
    laser-slot-distance top-apron-high 2 div add bed-w finger-lineto-noscrew
    laser-slot-distance bed-w lineto  % skip until mirror.

    0 bed-w finger-lineto-noscrew
    0 0 finger-lineto-noscrew

    0 bed-w measure-A pop pop
    laser-slot-distance bed-w [6 mm] measure-B pop pop

    stroke

    % Mounting holes for rounded lid
    1 dict begin
    /finger-len 15 mm def
    laser-slot-distance top-apron-high 2 div add acrylic-t add 0 moveto
    laser-slot-distance top-apron-high 2 div add acrylic-t add bed-w slot-lineto
    stroke
    end

    % Laser mount
    70 mm laser-edge-mount-distance moveto laser-footprint  % Mounting the laser
    %10 mm 36 mm 3 mm circle stroke      %cable laser wires

    % hsync photo diode
    laser-slot-distance laser-slot-h 2 div sub
    bed-w bed-w laser-slot-w sub 2 div sub 1.5 hsync-w mul add
    photo-diode-cutout

    % Laser slot
    laser-slot-distance laser-slot-h 2 div sub bed-w laser-slot-w sub 2 div moveto
    laser-slot-h laser-slot-w measure-box 3 mm rounded-corner-box

    do-angle-test {
     /scan-angle 40 def         % Something < 120, better less than 60
     /scan-redirect-mirror-distance 75 mm def  % Mirror distance
     polygon-mirror-assembly

     /scan-angle 50 def         % Something < 120, better less than 60
     /scan-redirect-mirror-distance 53 mm def  % Mirror distance
     polygon-mirror-assembly

     /scan-angle 55 def         % Something < 120, better less than 60
     /scan-redirect-mirror-distance 50 mm def  % Mirror distance
     polygon-mirror-assembly
    } {
     polygon-mirror-assembly
    } ifelse

    false {  % might need to go as we have LDGraphy now on the roleau
    gsave
      % LDGraphy text. Right now in front of the slot, but should be placed
      % on a panel covering the front part (not there yet)
      scan-engraving-cut
      laser-slot-distance top-apron-high 2 div add  % right of laser slot
      bed-w 2 div moveto
      90 rotate
      /Courier findfont ldgraphy-font-size scalefont setfont
      0 ldgraphy-font-size 0.8 mul neg rmoveto
      gsave (LDGraphy) center-show grestore
   grestore
    } if

   flush-measures
} def

/part-laser-height-gauge {
   (Height gauge) -25 mm polygon-mirror-high 4 mm add describe
   0 0 moveto
   20 mm 0 lineto
   0 polygon-mirror-high 1 mm add rlineto
   -30 mm 0 rlineto
   0 -2 mm rlineto
   10 mm 0 rlineto
   0 0 lineto
   stroke
} def

% The little mirror redirecting
/part-redirect-mirror {
    (Red. Mirror) -10 mm -10 mm describe
    3 dict begin
    /finger-len 3 mm def
    0 0 moveto
    scan-redirect-mirror-base 0 finger-rlineto-noscrew

    /h   laser-width 2 div scan-redirect-mirror-width 2 div sub def
    /w-r scan-redirect-mirror-base 2 div scan-redirect-mirror-width 2 div add def
    /w-l scan-redirect-mirror-base 2 div scan-redirect-mirror-width 2 div sub def

    scan-redirect-mirror-base h
    w-r 0   % control #2, pull down
    w-r laser-width 2 div curveto

    0 scan-redirect-mirror-width 2 div rlineto
    scan-redirect-mirror-width neg 0 rlineto
    0 scan-redirect-mirror-width 2 div neg rlineto

    w-l 0  % control #1
    0 h % control #2
    0 0 curveto
    stroke

    end
} def

/part-sidepanel {
  newpath
   % bottom part
   (Sidepanel) 35 mm 8 mm describe
   0 0 moveto current-measure-A
   inner-device-len 0 lineto [6 mm (inner-device-len)] current-measure-B

   % up right side (back)
   current-measure-A
   1 dict begin
      /finger-len 12 mm def
     inner-device-len
     inner-device-high acrylic-t add top-apron-high add groove-lineto
     [-8 mm] current-measure-B
   end

   % top part holding the lid
   inner-device-len laser-slot-distance sub top-apron-high 2 div add
   inner-device-high acrylic-t add top-apron-high add groove-lineto-noscrew

   % arc around towards baseline.
   inner-device-len laser-slot-distance sub top-apron-high 2 div add
   % Only doing 170 instead of 180 to avoid laser software choke.
   inner-device-high acrylic-t add top-apron-high 90 170 arc

    % end of mirror holder, down to top panel again.

    inner-device-len laser-slot-distance sub top-apron-high 2 div sub
    inner-device-high lineto

    % towards the front
    0 inner-device-high groove-lineto-noscrew

   % down left side (front)
   current-measure-A
   1 dict begin
      /finger-len 9 mm def
      0 inner-device-high inner-device-high front-cover-high sub sub lineto
      0 0 groove-lineto
      [-6 mm] current-measure-B
    end
    stroke

    % Mirror hole.
    gsave
      inner-device-len laser-slot-distance sub   % we go from the back
      inner-device-high acrylic-t add measure-A  % measure point: top of plate
      acrylic-t sub  % down-mirror-high already has acrylic-t included.
      down-mirror-high add [-10 mm] measure-B
      translate
      45 rotate
      down-mirror-slot {
        inner-cut
      } {
        measure-col
      } ifelse

      down-mirror-w 2 div neg 0 moveto down-mirror-w down-mirror-t box
      stroke

      1 dict begin
      /finger-len 3 mm def
      down-mirror-backing-width 2 div neg
      down-mirror-t down-mirror-backing-glue-thick add moveto
      down-mirror-backing-width 0 slot-rlineto
      stroke
      end

      stroke

   grestore

   bed-slide-high-bottom screw-r slide-holes
   gsave 0 bed-slide-high-bottom 3 mm add translate
   bed-slide-high-top screw-r slide-holes grestore

   % slots to hold top-panel
   inner-device-len laser-slot-distance sub
   inner-device-high moveto
   inner-device-len inner-device-high slot-lineto

   inner-device-len inner-device-high measure-A pop pop
   inner-device-len 0 [-6 mm (inner-device-high    )] measure-B pop pop
} def

/part-slide-rail-bottom {
    0 acrylic-t translate
    1 dict begin
    /finger-len 15 mm def
    % We make the top part with rounded corners, so that it is
    % easier to slide on the top-case on the bottom base.
    0 0 moveto
    inner-device-len 0 finger-rlineto-noscrew
    inner-device-len bed-slide-high-bottom   % towards top
    0 bed-slide-high-bottom                  % towards left
    5 mm arct   % rounded corner
    0 bed-slide-high-bottom
    0 0
    5 mm arct
    0 0 lineto
    stroke

    % see /subpart-drivetrain-base for corresponding fingers
    /finger-len 5 mm def
    drive-mount-pos stepper-height moveto
    0 stepper-height neg slot-rlineto stroke

    % mouting for bearing
    bearing-mount-pos stepper-height moveto
    0 stepper-height neg slot-rlineto stroke
    end

    bed-slide-high-bottom screw-r slide-holes stroke
} def

/part-slide-rail-top {
    0 0 moveto inner-device-len bed-slide-high-top box
    bed-slide-high-top screw-r slide-holes
    stroke
} def

/part-back {
   (Back) 30 mm 5 mm describe
   newpath
   0 0 moveto
   bed-w 0 lineto

   2 dict begin
      /back-height inner-device-high acrylic-t add top-apron-high add def
      /finger-len 12 mm def
      current-measure-A
      bed-w back-height finger-lineto % up
      [-6 mm] current-measure-B

      /finger-len 25 mm def
      bed-w neg 0 groove-rlineto  % flat back for now.

      /finger-len 12 mm def
      0 0 finger-lineto    % down

      % Place for the ethernet plug. Position copied from beaglebone footprint
      % A little larger for now to do some fitting.
      20.325 mm 1 mm add back-height 20 mm sub moveto
      19 mm 18 mm box

      % Place for the USB port. Rough placement right now, needs measurement
      5 mm 1 mm add back-height 8 mm sub moveto
      16 mm 6 mm box

      % 12V input DC jack.
      65 mm back-height 10 mm sub 8 mm 2 div circle
   end
   stroke

   % Top-panel matching.
   0 inner-device-high moveto current-measure-A
   bed-w 0 slot-rlineto stroke
   0 0 [20 mm (inner-device-high)] measure-B pop pop
   flush-measures


} def

/part-front {
   (Front) 30 mm 5 mm describe
   newpath
   0 0 moveto current-measure-A
   bed-w 0 lineto [-3 mm] current-measure-B

   current-measure-A
   1 dict begin
      /finger-len 9 mm def
      bed-w front-cover-high finger-lineto   % up
      [-6 mm] current-measure-B
   end

   bed-w neg 0 rlineto       % top accross

   1 dict begin
      /finger-len 9 mm def
     0 0 finger-lineto   % down
   end

   stroke
   flush-measures
} def

/part-down-mirror-back {
   (Down mirror backing) 5 mm 5 mm describe
   1 dict begin
   /finger-len 3 mm def   % same as used in part-sidepanel for this
   0 0 moveto bed-w 0 lineto
   bed-w down-mirror-backing-width finger-lineto-noscrew
   0 down-mirror-backing-width lineto
   0 0 finger-lineto-noscrew
   stroke

   % Show alignment lines to help centering mirror
   vector-engraving-cut
   3 mm down-mirror-backing-width 2 div down-mirror-w 2 div sub moveto
   bed-w 6 mm sub 0 rlineto
   3 mm down-mirror-backing-width 2 div down-mirror-w 2 div add moveto
   bed-w 6 mm sub 0 rlineto
   stroke
   outer-cut
   end
} def

/pcbframe-alignment-holes {
   gsave
   inner-cut
   bed-pcb-off-h 2 div
   bed-pcb-off-w acrylic-t add 2 div 0.5 mm circle

   bed-pcb-off-h 2 div
   bed-w bed-pcb-off-w acrylic-t add 2 div sub 0.5 mm circle

   bed-h bed-pcb-off-h 2 div sub
   bed-pcb-off-w acrylic-t add 2 div 0.5 mm circle

   bed-h bed-pcb-off-h 2 div sub
   bed-w bed-pcb-off-w acrylic-t add 2 div sub 0.5 mm circle
   stroke
   grestore
} def

/part-pcbframe {
  (PCB Frame) 30 mm 20 mm describe
  % Frame to place PCB in.
  gsave
   % Outer frame
   0 acrylic-t moveto
   bed-h bed-w acrylic-t 2 mul sub box

   pcbframe-alignment-holes

   bed-pcb-off-h bed-pcb-off-w measure-A pop pop
   bed-pcb-off-h bed-pcb-off-w pcb-w add [-6 mm (pcb-w)] measure-B pop pop
   bed-pcb-off-h bed-pcb-off-w measure-A pop pop
   bed-pcb-off-h pcb-h add bed-pcb-off-w [10 mm (pcb-h)] measure-B pop pop

   % Inner frame
   bed-pcb-off-h bed-pcb-off-w translate
   0 0 moveto pcb-h pcb-w box

   % Corner reliefs
   0 0 0.8 mm circle
   pcb-h 0 0.8 mm circle
   0 pcb-w 0.8 mm circle
   pcb-h pcb-w 0.8 mm circle
   stroke

   vector-engraving-cut
   /Courier findfont 2.5 mm scalefont setfont
   pcb-h pcb-w translate 90 rotate
   0 1 pcb-w-mm {
       1 dict begin
       /i exch def
       i mm neg 1 mm moveto
       i 5 mod 0 eq { i 10 mod 0 eq { -7 mm } { -4.5 mm } ifelse } { -3 mm } ifelse 0 exch rlineto stroke
       i 10 mod 0 eq {
	   gsave
           i mm neg 0.5 mm sub -4.5 mm translate 90 rotate
	   0 0 moveto
	   i 10 div cvi 5 string cvs show
	   grestore
       } if
      end
   } for

   0 1 pcb-h-mm {
       1 dict begin
       /i exch def
       i mm -1 mm exch moveto
       i 5 mod 0 eq { i 10 mod 0 eq { 7 mm } { 4.5 mm } ifelse } { 3 mm } ifelse 0 rlineto stroke
       i 10 mod 0 eq {
	   gsave
           i mm 0.5 mm add 4.5 mm exch translate 90 rotate
	   0 0 moveto
	   i 10 div cvi 5 string cvs show
	   grestore
       } if
       end
   } for
  grestore
} def

/subpart-drive-holder {
  gsave
  0 0 moveto
  bed-drive-h 0 rlineto
  current-measure-A
  0 bed-drive-w rlineto
  [-3 mm] current-measure-B
  bed-drive-h neg 0 rlineto
  0 bed-drive-w neg finger-rlineto-noscrew
  stroke

  bed-drive-screw-top bed-drive-w 2 div translate
  1 dict begin
  /nut-r bed-drive-screw-r bed-drive-wobble add 0.5 mm add def
  0 0 nut-r circle stroke
  0 nut-r neg moveto
  nut-r 1 mm add nut-r 2 mul box stroke
  end
  grestore
} def

/part-sled {
  (Sled) 90 mm 60 mm describe
  3 dict begin
  % Both springs should be the same length (for same string force).
  % The front-one needs its 'hump' to be in the middle, as this is the
  % center of gravity that is just supported when the sled is fully
  % extended.
  /spring-len bed-h 4 div 10 mm sub def
  /center-spring-pos bed-h 2 div spring-len sub 5 mm add def
  /rounded 3 mm def

  %0 0 moveto bed-h bed-w 3 mm rounded-corner-box  % the sled
  gsave
  % Messed up, the below needs to be rotated. Quick hack around:
  bed-h bed-w translate 180 rotate

  outer-cut

  % Starting at top left. Move to the beginning of spring, then arc left around
  center-spring-pos bed-w moveto
  rounded bed-w rounded sub rounded 90 180 arc  % top right arc

  0 rounded lineto
  rounded rounded rounded 180 270 arc    % bottom left arc

  bed-h rounded sub 0 lineto  % bottom left-right
  currentpoint rounded add rounded -90 0 arc   % rounded up
  bed-h bed-w lineto                           % up

  % Little connector piece between springs.
  center-spring-pos spring-len add bed-w moveto
  bed-h 2 div spring-len sub 0 rlineto stroke

  center-spring-pos  bed-w spring-len x-spring    % center spring.
  bed-h spring-len sub bed-w spring-len x-spring  % spring at end of part
  stroke

  % some rough centimeter scale on the bed
  vector-engraving-cut
  0 10 pcb-w-mm {
      mm bed-pcb-off-w add bed-pcb-off-h exch moveto
      pcb-h 0 rlineto
  } for
  0 10 pcb-h-mm {
      mm bed-pcb-off-h add bed-pcb-off-w moveto
      0 pcb-w rlineto
  } for
  stroke

  grestore

  pcbframe-alignment-holes

  %gsave part-pcbframe grestore  % testing

  0 0 measure-A pop pop
  bed-h 0 [6 mm (bed-h)] measure-B pop pop

  0 0 measure-A pop pop
  0 bed-w [-6 mm (bed-w)] measure-B pop pop

  % endswitch trigger thing: slots in the sled.
  /finger-len 3 mm def
  end-switch-from-back-of-sled
  bed-w 2 div end-switch-offset add acrylic-t 2 div sub moveto
  end-switch-trigger-len 0 slot-rlineto
  stroke

  % measurement endswitch trigger
  end-switch-from-back-of-sled
  bed-w 2 div end-switch-offset add measure-A pop pop
  end-switch-from-back-of-sled bed-w [-30 mm] measure-B pop pop

  0 bed-w 2 div measure-A pop pop
  bed-drive-pos bed-w 2 div [-40 mm (bed-drive-pos)] measure-B pop pop

  /finger-len 6 mm def

  % slots on sled for drive train.
  gsave
    bed-drive-pos bed-drive-screw-w 2 div sub
    bed-w 2 div bed-drive-w 2 div sub moveto
    0 bed-drive-w slot-rlineto stroke
  grestore

  gsave
    bed-drive-pos bed-drive-screw-w 2 div add acrylic-t add
    bed-w 2 div bed-drive-w 2 div sub moveto
    0 bed-drive-w slot-rlineto stroke
  grestore

  gsave
    % Finger hole to poke out PCB
    /finger-hole-r 5 mm def
    bed-h bed-pcb-off-h sub finger-hole-r sub 1 mm sub bed-w 2 div translate
    1 1.5 scale 0 0 finger-hole-r circle stroke
  grestore

  end
} def

/part-sled-misc {
  (Sled Misc) 0 0 describe
  1 dict begin
  % drive holder. this should be a separate sub-part
  gsave
   /finger-len 6 mm def
   acrylic-t 0 translate
   subpart-drive-holder flush-measures
   bed-drive-h cut-dist add 6 mm add 0 translate
   subpart-drive-holder clear-measures
   stroke
  grestore

  % Moving part holding the nut
  gsave
   acrylic-t bed-drive-w cut-dist add translate

   % nut holder. That should be a separate sub-part.
   bed-drive-wobble 0 moveto
   bed-drive-h bed-drive-wobble sub bed-drive-w measure-box 2 mm rounded-corner-box
   flush-measures

   % Placing the nut.
   gsave
     % The nut is a polygon that we need to turn a little. So move to the center
     % and rotate our canvas.
     bed-drive-screw-top bed-drive-w 2 div translate
     0 0 moveto 360 12 div rotate inner-cut 6 bed-drive-screw-r n-polygon stroke
   grestore
  grestore

  % Endswitch trigger block, plugged into the sled.
  gsave
     /finger-len 3 mm def
     acrylic-t 30 mm add cut-dist add bed-drive-w cut-dist add translate
     newpath
     15 mm 0 moveto
     0 end-switch-trigger-len finger-rlineto-noscrew
     -15 mm 0 rlineto
     0 0 lineto
     15 mm 0 lineto stroke
  grestore

  % since this is fluorescent material, cut out the hsync collector here.
  gsave
     % more fudging with moving to a free space...
     30 mm cut-dist 2 mul add    bed-w hsync-w sub translate
     0 0 moveto hsync-len hsync-w box
     0 hsync-w cut-dist add neg moveto hsync-len hsync-w box
     % ... do it a couple of times, as some will fall through the cracks :)
     0 hsync-w cut-dist add 2 mul neg moveto hsync-len hsync-w box
     0 hsync-w cut-dist add 3 mul neg moveto hsync-len hsync-w box
     stroke
  grestore
  end
} def

% base for the inner parts that hold motor and bearing.
/subpart-drivetrain-base {
    2 dict begin
    0 0 moveto  current-measure-A
    /finger-len drive-train-fingers def
    /width bed-w acrylic-t 2 mul sub def
    width 0 finger-rlineto-noscrew [-6 mm] current-measure-B

    /finger-len 5 mm def
    0 stepper-height finger-rlineto-noscrew
    width neg 0 rlineto
    0 stepper-height neg finger-rlineto-noscrew
    stroke

    % Endswitch
    width 2 div end-switch-offset add
    stepper-height 15 mm sub moveto current-measure-A
    end-switch stroke
    bed-w acrylic-t sub stepper-height 15 mm sub [-10 mm ] measure-B pop pop flush-measures
    end
} def

/part-motor-holder {
   (Motor) 5 mm 5 mm describe
    1 dict begin
    /width bed-w acrylic-t 2 mul sub def
    subpart-drivetrain-base
    width 2 div stepper-height sub stepper-height 2 div 5 mm circle stroke %cable
    gsave
    inner-cut
    width 2 div stepper-height 2 div translate
    0 0 stepper-center-plate-dia 2 div circle stroke
    stepper-mount-screw stepper-mount-screw stepper-screw-dia 2 div circle stroke
    stepper-mount-screw neg stepper-mount-screw stepper-screw-dia 2 div circle stroke
    stepper-mount-screw neg stepper-mount-screw neg stepper-screw-dia 2 div circle stroke
    stepper-mount-screw stepper-mount-screw neg stepper-screw-dia 2 div circle stroke
    grestore
    end
} def

/part-bearing-holder {
   (Bearing) 10 mm 5 mm describe
    1 dict begin
    /width bed-w acrylic-t 2 mul sub def
    subpart-drivetrain-base
    width 2 div stepper-height 2 div acme-dia 2 div circle stroke

    width 2 div end-switch-offset add 8 mm add
    stepper-height 2 div 4 mm add 2.5 mm circle stroke %cable
    end
} def

/sled-range-visual {
  gsave part-sled grestore
  part-pcbframe
  gsave bearing-mount-pos acrylic-t 1.5 mul sub bed-drive-pos sub 0 translate part-sled grestore
  clear-measures
} def

/part-bottom {
  (Bottom) 40 mm 10 mm describe
  acrylic-t neg acrylic-t neg moveto
  inner-device-len acrylic-t 2 mul add bed-w acrylic-t 2 mul add measure-box box

  1 dict begin
  /finger-len 15 mm def

  0 0 moveto inner-device-len 0 slot-rlineto stroke  % lower part
  inner-device-len bed-w moveto inner-device-len neg 0 slot-rlineto stroke

  /finger-len drive-train-fingers def
  % mounting for motor
  drive-mount-pos bed-w moveto
  0 bed-w neg slot-rlineto stroke

  drive-mount-pos bed-w 2 div moveto motor-footprint

  0 bed-w 2 div measure-A pop pop
  drive-mount-pos bed-w 2 div [20 mm (drive-mount-pos)] measure-B pop pop

  % mouting for bearing
  bearing-mount-pos bed-w moveto
  0 bed-w neg slot-rlineto stroke

  0 bed-w 2 div measure-A pop pop
  bearing-mount-pos bed-w 2 div [-20 mm (bearing-mount-pos)] measure-B pop pop

  % finger holes to better access side screws
  true {
    22 mm acrylic-t 2 mul add 8 mm slide-holes stroke
    gsave 0 bed-w translate -22 mm acrylic-t 2 mul sub 8 mm slide-holes stroke grestore
  } if
  end

  %sled-range-visual

  bed-drive-pos bed-w 2 div measure-A pop pop
  bearing-mount-pos acrylic-t 1.5 mul sub bed-w 2 div [0 mm (sled range)] measure-B pop pop

  flush-measures
} def

/part-lid {
    (Lid) 5 mm 5 mm describe
    3 dict begin
    /ldgraphy-font-size 22 mm laser-slot-w mul 104 mm div def
    /finger-len 20 mm def
    /long-part laser-slot-distance top-apron-high 2 div sub def
    /hinge-len 2 3.1415926 mul top-apron-high mul 4 div def % quarter turn

    0 0 moveto
    long-part 0 finger-lineto-noscrew
    0 acrylic-t neg rlineto
    hinge-len 0 rlineto

    /finger-len 15 mm def
    0 bed-w acrylic-t 2 mul add finger-rlineto-noscrew

    hinge-len neg 0 rlineto  % rouleau
    0 acrylic-t neg rlineto

    /finger-len 20 mm def
    0 bed-w finger-lineto-noscrew

    /finger-len 25 mm def
    0 0 finger-lineto
    stroke

    % living hinge in front part
    gsave
    long-part hinge-len add acrylic-t neg translate
    90 rotate

    % First the text, as PostScript does not allow composition.
    scan-engraving-cut
    /Courier findfont ldgraphy-font-size scalefont setfont
    bed-w acrylic-t 2 mul add 2 div ldgraphy-font-size 2 div moveto
    (LDGraphy) center-show

    simple-cut  % no kerf correction.
    0.5 mm 0.5 mm moveto
    bed-w acrylic-t 2 mul add 1 mm sub
    hinge-len 1 mm sub { wave-bend } kerf-bend
    stroke

    outer-cut
    grestore

    end

    gsave
    1 mm bed-w 1 mm sub moveto
    1 -1 scale    % flip over, as we mount it on the underside.
    beaglebone-black-footprint
    grestore
} def

% ---------------------------------------------------------------------
% ------------------- Assembly ----------------------------------------
% ---------------------------------------------------------------------

acrylic-t cut-dist add cut-dist translate

print-bottom {
  0 acrylic-t translate   % to have space for the fingers
  part-bottom
  0 bed-w acrylic-t add cut-dist add translate
} if

print-bottom-drivetrain {
  0 acrylic-t translate   % to have space for the fingers
  part-motor-holder
  gsave bed-w acrylic-t 2 mul add cut-dist add 0 translate part-bearing-holder grestore
  0 28.2 mm cut-dist add translate
} if

print-slide-rail-bottom {
  (Slide Rail Bottom) 50 mm 8 mm describe
  part-slide-rail-bottom
  0 bed-slide-high-bottom cut-dist add translate part-slide-rail-bottom
  0 bed-slide-high-bottom cut-dist add translate
} if

print-sled {
  0 1 mm translate   % for the spring 'bump'
  part-sled flush-measures
  gsave bed-h cut-dist add 0 translate part-sled-misc grestore
  0 bed-w cut-dist 2 mul add translate
} if

print-slide-rail-top {
  part-slide-rail-top
  0 bed-slide-high-top cut-dist add translate part-slide-rail-top
  0 bed-slide-high-top cut-dist add translate
} if

print-sidepanels {
  % Two side-panels, turned around and printed in place
  part-sidepanel flush-measures
  gsave inner-device-len side-cut-distance translate 180 rotate part-sidepanel
  clear-measures grestore
  % move up
  0 side-cut-distance cut-dist add translate
} if

print-front-back {
  0 0 moveto part-front stroke % front-piece
  gsave 0 30 mm translate part-down-mirror-back grestore
  gsave bed-w acrylic-t 2 mul add cut-dist add 0 translate
        part-back stroke grestore % back piece
  0 inner-device-high acrylic-t 2 mul add top-apron-high add cut-dist add translate
} if

print-toppanel {
  0 acrylic-t translate   % to have space for the fingers
  0 0 moveto part-toppanel

  %sled-range-visual

  gsave
  inner-device-len scan-redirect-mirror-base sub bed-w 2 div 30 mm sub translate
  part-redirect-mirror
  grestore

  gsave
  inner-device-len 25 mm sub bed-w 2 div 5 mm add translate
  part-laser-height-gauge
  grestore


  false {
     % extra raiser board for the mirror
    gsave
    inner-device-len 11 mm sub bed-w 2 div translate 90 rotate
    (Extra raiser) -20 mm 0 describe
    true  % cut outline
    0 0 polygon-mirror-footprint stroke
    grestore
  } if
  0 bed-w 2 acrylic-t mul add translate
} if

print-pcbframe {
  gsave
  part-pcbframe flush-measures

  %% somewhat unrelated
  % extra raiser board for the mirror. Use empty space in the area in the
  % middle. Since that often needs to be a thinner shim, this is perfect
  % as this is typically printed on 1mm
  pcb-h 25 mm sub pcb-w 20 mm sub translate 90 rotate
  true  % cut outline
  0 0 polygon-mirror-footprint stroke
  grestore

  0 bed-w cut-dist add translate
} if

print-lid {
   0 acrylic-t translate
   part-lid
} if

print-TEST-finger-slot {
    0 acrylic-t cut-dist add translate
    1 dict begin

    /Helvetica-Bold findfont 3 mm scalefont setfont
    vector-engraving-cut
    5 mm 16 mm moveto (Slot Sample) show
    12 mm 32 mm moveto (10 mm) show
    8 mm 1 mm moveto (5 mm) show
    outer-cut

    outer-cut
    /finger-len 5 mm def
    0 0 moveto
    55 mm 0 groove-rlineto-noscrew
    0 35 mm rlineto

    /finger-len 10 mm def
    -55 mm 0 groove-rlineto
    0 -35 mm rlineto
    stroke

    0 0 moveto
    /finger-len 5 mm def
    0 10 mm rmoveto
    55 mm 0 slot-rlineto

    0 0 moveto
    /finger-len 10 mm def
    0 20 mm rmoveto
    55 mm 0 slot-rlineto

    33 mm 29 mm photo-diode-cutout
    10 mm 29 mm moveto hsync-slot

    % Finger sample
    /Helvetica-Bold findfont 3 mm scalefont setfont
    vector-engraving-cut 65 mm 12 mm moveto (Finger Sample) show outer-cut

    60 mm 0 moveto
    /finger-len 10 mm def
    55 mm 0 finger-rlineto
    0 20 mm rlineto
    /finger-len 5 mm def
    -55 mm 0 finger-rlineto-noscrew
    0 -20 mm rlineto
    stroke
    end
    0 50 mm translate
} if

print-TEST-x-spring {
    gsave
    /spring-len bed-h 4 div 10 mm sub def
    outer-cut
    0 0 moveto
    0 10 mm lineto
    5 mm 0 rlineto stroke
    5 mm 10 mm spring-len x-spring
    outer-cut
    60 mm 10 mm lineto
    60 mm 0 mm lineto
    0 0 lineto
    stroke
    grestore
} if

stroke
showpage
